#!/bin/sh

# Copyright 2007-2009 Heinz Wiesinger, Amsterdam, The Netherlands
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# nvidia-switch utility 0.8.2
#
# A tool to switch between nvidia-binary-driver and stock xorg-driver
# if both are installed in parallel.

set -e

ROOT="/"
CWD=$(pwd)
LIBSUFFIX="LIBDIRSUFFIX" # This will be replaced in the build script
INC="${ROOT}usr/include/GL"
LIB="${ROOT}usr/lib${LIBSUFFIX}"
XMOD="${LIB}/xorg/modules"
XLIB="$XMOD/extensions"
NV_VERSION='PKGVERSION' # This will be replaced in the build script
GL_VERSION="1.2"

remove_link(){
if [ -L "$1" ]; then
  rm -f "$1"
fi
}

remove_existing(){
if [ -e "$1" ]; then
  rm -f $1
fi
}

move_existing(){
if [ -e "$1" ]; then
  mv $1 $2
fi
}

setup_link(){
  if [ "$1" = "mv" ]; then
    mv "$2" "$3"
  else
    rm -f $2
  fi
  ln -s "$3" "$2"
}

# Move and rename files in /usr/include
# $1 = from
# $2 = to
incs(){
cd "$INC"

if [ "$2" = "cleanup" ]; then
  CMD="mv"
  THREE="$1"
else
  CMD="ln -s"
  THREE="$2"
fi

for i in glxext.h glext.h glx.h gl.h; do
  if [ -L "$i" ]; then
    rm -f "$i"
    $CMD "$i-$THREE" "$i"
  elif [ -e "$i" ]; then
    if [ "$2" = "cleanup" ]; then
      rm -f "$i-$1"
    else
      mv "$i" "$i-$1"
      ln -s "$i-$THREE" "$i"
    fi
  else
    $CMD "$i-$THREE" "$i"
  fi
done

cd "$CWD"
}

libs(){
for i in libglx.la; do
  if [ "$1" = "nvidia" ]; then
    #If this library exists, move it to *-xorg
    move_existing "$XLIB/$i" "$XLIB/$i-xorg"
  else
    #If .so does not exist, and *-xorg does, then remove the -xorg
    if [ -e "$XLIB/$i" ]; then
      remove_existing "$XLIB/$i-xorg"
    else
      move_existing "$XLIB/$i-xorg" "$XLIB/$i"
    fi
  fi
done
}

libs_basic(){
for i in libGL.so libGLcore.so; do
  #if this link exists, remove it, if it's a file, move it to *.nvidia
  if [ -L "$LIB/$i.$NV_VERSION" ]; then
    rm -f "$LIB/$i.$NV_VERSION"
  elif [ -e "$LIB/$i.$NV_VERSION" ]; then
    mv "$LIB/$i.$NV_VERSION" "$LIB/$i.$NV_VERSION-nvidia"
  fi
  remove_link "$LIB/$i.1"
done
}

libgl_nvidia(){
#if libGL.so.$GL_VERSION does exist, move it to libGL.so.$GL_VERSION-xorg, as it conflicts if nvidia's libGL.so
# then remove the libGL.so.1 symlink and create a new one pointing to nvidia's libGL.so
if [ -e "$LIB/libGL.so.$GL_VERSION" ]; then
  cd "$LIB"
  mv libGL.so.$GL_VERSION libGL.so.$GL_VERSION-xorg
  setup_link "" "libGL.so.1" "libGL.so.$NV_VERSION"
  cd "$CWD"
fi
}

libgl_xorg(){
#if libGL.so.$GL_VERSION does not exist and libGL.so.$GL_VERSION-xorg does, move it to libGL.so.$GL_VERSION
# then remove the libGL.so.1 symlink and create a new one pointing to nvidia's libGL.so
if [ -e "$LIB/libGL.so.$GL_VERSION" ]; then
  remove_existing "$LIB/libGL.so.$GL_VERSION-xorg"
else
  if [ -e "$LIB/libGL.so.$GL_VERSION-xorg" ]; then
    cd "$LIB"
    mv libGL.so.$GL_VERSION-xorg libGL.so.$GL_VERSION
    ln -s libGL.so.$GL_VERSION libGL.so.1
    cd "$CWD"
  fi
fi
}


libglcore_nvidia(){
#If libGLcore.so.$NV_VERSION-nvidia does exists, then remove the -nvidia and make it usable that way
if [ -e "$LIB/libGLcore.so.$NV_VERSION-nvidia" ]; then
  cd "$LIB"
  rm -f libGLcore.so.$NV_VERSION libGLcore.so.1
  ln -s libGLcore.so.$NV_VERSION-nvidia libGLcore.so.$NV_VERSION
  ln -s libGLcore.so.$NV_VERSION libGLcore.so.1
  cd "$CWD"
fi
}

lib_nvidia(){
for i in libGL.so libglx.so; do
  if [ "$i" = "libGL.so" ]; then
    cd "$LIB"
  elif [ "$i" = "libglx.so" ]; then
    cd "$XLIB"
  fi
  #If libGL.so.$NV_VERSION-nvidia does exists, then remove the -nvidia and make it usable that way
  if [ -e "$i.$NV_VERSION-nvidia" ]; then
    setup_link "" "$i.$NV_VERSION" "$i.$NV_VERSION-nvidia"
  fi
  cd "$CWD"
done
}

libglx_base(){
for i in $(ls libglx.so* | grep -v -); do
  if ! [ "$i" = "libglx.so.$NV_VERSION" ]; then
    rm -f $i
  fi
done
ln -s libglx.so.$NV_VERSION libglx.so
}

libglx_nvidia(){
# if libglx.so is not a link, back it up to *-xorg, and create a symlink to nvidia's one
# if it's a link, then remove it and create a new one.
if ! [ -L "$XLIB/libglx.so" ]; then
  cd $XLIB
  mv libglx.so libglx.so-xorg
  libglx_base
else
  cd $XLIB
  libglx_base
fi
cd $CWD
}

libglx_xorg(){
if [ "$1" = "xorg" ]; then
  CMD="ln -s"
else
  CMD="mv"
fi

#if this library exists, move it to *-nvidia
if [ -L "$XLIB/libglx.so.$NV_VERSION" ]; then
  rm $XLIB/libglx.so.$NV_VERSION
elif [ -e "$XLIB/libglx.so.$NV_VERSION" ]; then
  mv $XLIB/libglx.so.$NV_VERSION $XLIB/libglx.so.$NV_VERSION-nvidia
fi

# if libglx.so exists and is a link, remove it and create a new one
# If it does exists and is not a link, remove the old one, and create the new one.
# if it does not exist, create a symlink
if [ -L "$XLIB/libglx.so" ]; then
  cd $XLIB
  rm libglx.so
  $CMD libglx.so-xorg libglx.so
elif [ -e "$XLIB/libglx.so" ]; then
  if [ "$1" = "xorg" ]; then
    cd $XLIB
    setup_link "mv" "libglx.so" "libglx.so-xorg"
  else
    remove_existing "$XLIB/libglx.so-xorg"
  fi
else
  cd $XLIB
  $CMD libglx.so-xorg libglx.so
fi
cd $CWD
}

nvidia_ldconfig(){
/sbin/ldconfig
#Generate correct symink for that lib
/sbin/ldconfig -l $1

if [ "$2" = "xorg" ]; then
  #Remove so-link, recreated by ldconfig
  cd $LIB
  remove_link "libGLcore.so.1"
  cd $CWD
fi
}

check(){
  echo -n "checking $2...."
  if [ -e "$1/$2" ]; then
    if [ "$3" = "exist" ]; then
      echo "ERROR: $1/$2 does exist!!!!!"
    else
      echo -n "exists"
      if [ -h "$1/$2" ]; then
        echo "(link)"
        echo -n " points to:"
        ls -o "$1/$2" | cut -d ">" -f 2
      else
        if [ "$3" = "link" ]; then
          echo " (!)"
        else
          echo ""
        fi
      fi
    fi
  else
    if [ "$3" = "exist" ]; then
      echo "does not exist"
    else
      echo "ERROR: $1/$2 does not exist!!!!!"
    fi
  fi
}

check_includes(){
  if [ "$1" = "cleanup" ]; then
    cleanup=""
    cleanup2="exist"
  else
    cleanup="link"
    cleanup2=""
  fi

  for i in glext.h glxext.h gl.h glx.h; do
    check $INC $i $cleanup
    check $INC $i-nvidia
    check $INC $i-xorg $cleanup2
    echo ""
  done
}

check_glcore(){
  if [ "$1" = "nvidia" ]; then
    CHECK="link"
  else
    CHECK="exist"
  fi

  for i in libGLcore.so.1 libGLcore.so.$NV_VERSION libGLcore.so.$NV_VERSION-nvidia; do
    if [ "$i" = "libGLcore.so.1" ]; then
      check $LIB $i $CHECK
    elif [ "$i" = "libGLcore.so.$NV_VERSION" ]; then
      check $LIB $i $CHECK
    else
      check $LIB $i
    fi
  done

  echo ""
}

check_glx(){
  if [ "$1" = "xorg" ]; then
    xorg="exist"
    cleanup="link"
    cleanup2=""
    EXT="-xorg"
  elif [ "$1" = "cleanup" ]; then
    xorg="exist"
    cleanup=""
    cleanup2="exist"
    EXT="-xorg"
  else
    xorg="link"
    cleanup="link"
    cleanup2=""
    EXT=""
  fi

  for i in libglx.la libglx.so libglx.so.$NV_VERSION libglx.so.$NV_VERSION-nvidia \
    libglx.so-xorg libglx.la-xorg; do
    if [ "$i" = "libglx.so" ]; then
      check $XLIB $i $cleanup
    elif [ "$i" = "libglx.so-xorg" ]; then
      check $XLIB $i $cleanup2
    elif [ "$i" = "libglx.so.$NV_VERSION" ]; then
      check $XLIB $i $xorg
    elif [ "$i" = "libglx.la$EXT" ]; then
      check $XLIB $i "exist"
    else
      check $XLIB $i
    fi
  done

  echo ""
}

check_wfb(){
  for i in libnvidia-wfb.so.1 libnvidia-wfb.so.$NV_VERSION; do
    if [ "$i" = "libnvidia-wfb.so.1" ]; then
      check $XMOD $i "link"
    else
      check $XMOD $i
    fi
  done
}

check_gl(){
  if [ "$1" = "nvidia" ]; then
    nvidia="link"
    EXT=""
  else
    nvidia="exist"
    EXT="-xorg"
  fi

  for i in libGL.la libGL.so libGL.so.1 libGL.so.$NV_VERSION libGL.so.$NV_VERSION-nvidia \
    libGL.so.$GL_VERSION libGL.so.$GL_VERSION-xorg; do
    if [ "$i" = "libGL.so" ]; then
      check $LIB $i "link"
    elif [ "$i" = "libGL.so.1" ]; then
      check $LIB $i "link"
    elif [ "$i" = "libGL.so.$NV_VERSION" ]; then
      check $LIB $i $nvidia
    elif [ "$i" = "libGL.so.${GL_VERSION}${EXT}" ]; then
      check $LIB $i "exist"
    else
      check $LIB $i
    fi
  done

  echo ""
}

nvidia_check(){
  check_includes

  check_gl "nvidia"

  check_glcore "nvidia"

  check_glx

  check_wfb
}

xorg_check(){
  check_includes

  check_gl

  check_glcore

  check_glx "xorg"

  check_wfb
}

cleanup_check(){
  check_includes "cleanup"

  check_gl

  check_glcore

  check_glx "cleanup"

  check_wfb
}

nvidia(){
  echo $'Switching to nvidia-driver files!\n'
  echo "Make sure the nvidia driver is ENABLED in /etc/X11/xorg.conf."
  echo "Otherwise, this may lead to improperly working drivers."

  incs "xorg" "nvidia"
  lib_nvidia
  libgl_nvidia
  libglcore_nvidia
  libs "nvidia"
  libglx_nvidia

  LD_NVIDIA="${LIB}/libGL.so.$NV_VERSION-nvidia"
  nvidia_ldconfig $LD_NVIDIA
}

xorg(){
  echo $'Switching to stock xorg files.\n'
  if [ "$1" = "cleanup" ]; then
    echo $'Cleaning up symlinks.\n'
  fi
  echo "Make sure the nvidia driver is DISABLED in /etc/X11/xorg.conf."
  echo "Otherwise, this may lead to improperly working drivers."

  if [ "$1" = "cleanup" ]; then
    incs "xorg" "cleanup"
  else
    incs "xorg" "xorg"
  fi

   libs_basic
   libgl_xorg
   libs ""
   if [ "$1" = "cleanup" ]; then
     libglx_xorg ""
   else
     libglx_xorg "xorg"
   fi

   LD_NVIDIA="${LIB}/libGL.so.1.2"
   nvidia_ldconfig $LD_NVIDIA "xorg"
}

usage(){
  echo "Usage:"
  echo " --nvidia		Switch to nvidia driver files"
  echo " --xorg			Switch to stock xorg files"
  echo " --cleanup		Switch to stock xorg files and remove all created symlinks"
  echo " --install		Switch to nvidia driver files"
  echo "			  This is used on installation to handle installroot correctly"
  echo "			  Please use --nvidia for after-install switches instead"
  echo " --check-nvidia		Check if everything is setup correctly for nvidia's driver"
  echo " --check-xorg		Check if everything is setup correctly for xorg's driver"
  echo " --check-cleanup	Check if everything has been cleaned up correctly"
  echo " --help			Show this help message"
}

if [ "$1" = '--nvidia' ]; then
  nvidia
elif [ "$1" = '--install' ]; then
  ROOT=""
  CWD=$(pwd)
  if [ -e "${ROOT}usr/lib64" ]; then
    LIBSUFFIX="64"
  else
    LIBSUFFIX=""
  fi
  INC="${ROOT}usr/include/GL"
  LIB="${ROOT}usr/lib${LIBSUFFIX}"
  XMOD="${LIB}/xorg/modules"
  XLIB="$XMOD/extensions"
  nvidia
elif [ "$1" = '--xorg' ]; then
  xorg ""
elif [ "$1" = '--check-nvidia' ]; then
  nvidia_check
elif [ "$1" = '--check-xorg' ]; then
  xorg_check
elif [ "$1" = '--check-cleanup' ]; then
  cleanup_check
elif [ "$1" = '--cleanup' ]; then
  xorg "cleanup"
elif [ "$1" = '--help' ]; then
  usage
else
  usage
fi
